#include "filesystem.hpp"
#include "foreach.hpp"
#include "hex_geometry.hpp"
#include "raster.hpp"
#include "surface_cache.hpp"
#include "terrain.hpp"
#include "tile.hpp"

namespace hex {

namespace {
int prandom(const location& loc)
{
	const unsigned int a = (loc.x() + 92872873) ^ 918273;
	const unsigned int b = (loc.y() + 1672517) ^ 128123;
	return a*b + a + b;
}
}

tile::tile(const location& loc, const_terrain_ptr terrain)
  : loc_(loc), terrain_(terrain)
{
	const_terrain_ptr underlying = terrain->underlying() ? terrain->underlying() : terrain;
	if(underlying->get_textures().empty() == false) {
		const int index = prandom(loc)%underlying->get_textures().size();
		textures_.push_back(underlying->get_textures()[index]);
	}

	if(underlying != terrain && terrain->get_textures().empty() == false) {
		const int index = prandom(loc)%terrain->get_textures().size();
		overlays_.push_back(terrain->get_textures()[index]);
	}

	foreach(const tile*& t, adjacent_) {
		t = NULL;
	}
}

namespace {
std::string build_adjacent_filename(const_terrain_ptr t, int n1, int n2) {
	std::string fname = "/terrain/" + t->name();
	while(n1 != n2) {
		fname += "-" + direction_abbreviation(static_cast<DIRECTION>(n1));
		n1 = (n1+1)%6;
	}

	return fname + ".png";
}
}

void tile::set_adjacent(const tile** adj)
{
	for(int n = 0; n != 6; ++n) {
		adjacent_[n] = adj[n];
	}

	bool filled_adjacent[6];
	const_terrain_ptr adj_terrain[6];
	for(int n = 0; n != 6; ++n) {
		filled_adjacent[n] = true;
		if(adjacent_[n]) {
			adj_terrain[n] = adjacent_[n]->terrain();
			while(adj_terrain[n]->underlying()) {
				adj_terrain[n] = adj_terrain[n]->underlying();
			}

			//if the adjacent terrain should overlap us, mark
			//us as having to fill out their adjacency
			if(adj_terrain[n] && adj_terrain[n]->overlay_order() > terrain_->overlay_order()) {
				filled_adjacent[n] = false;
			}
		}
	}

	int start_pos = 0, end_pos = 5;
	if(adj_terrain[start_pos] && adj_terrain[start_pos] == adj_terrain[end_pos]) {
		start_pos = 5;
		end_pos = 4;
		while(adj_terrain[start_pos] == adj_terrain[end_pos] && start_pos != 0) {
			--start_pos;
			--end_pos;
		}
	}

	while(std::count(filled_adjacent, filled_adjacent + 6, true) != 6) {
		if(filled_adjacent[start_pos] == false) {
			assert(adj_terrain[start_pos]);

			int n = 1;
			while(n != 6 && adj_terrain[start_pos] == adj_terrain[(start_pos+n)%6]) {
				++n;
			}

			//try calculate the largest set of adjacent
			//terrains that there is an image defined for.
			while(n != 1 && !graphics::surface_cache::get(build_adjacent_filename(adj_terrain[start_pos], start_pos, (start_pos+n)%6))) {
				--n;
			}

			graphics::texture adj_texture(graphics::texture::get(build_adjacent_filename(adj_terrain[start_pos], start_pos, (start_pos+n)%6)));

			if(adj_texture.valid()) {
				textures_.push_back(adj_texture);
			}

			//mark all the adjacency that we've taken care of
			for(int m = 0; m != n; ++m) {
				filled_adjacent[(start_pos+m)%6] = true;
			}
		}
		start_pos = (start_pos+1)%6;
	}
}

void tile::draw() const
{
	const int x = tile_pixel_x(loc_) + HexWidth/2;
	const int y = tile_pixel_y(loc_) + HexHeight/2;

	foreach(const graphics::texture& t, textures_) {
		graphics::blit_texture(t, x - t.width()/2, y - t.height()/2);
	}
}

void tile::draw_overlay() const
{
	const int x = tile_pixel_x(loc_) + HexWidth/2;
	const int y = tile_pixel_y(loc_) + HexHeight/2;

	foreach(const graphics::texture& t, overlays_) {
		graphics::blit_texture(t, x - t.width()/2, y - t.height()/2);
	}
}

}
